<?php
/**
 * Font controller
 *
 */
class Controller extends IError
{
	/**
	 * Processed module name
	 *
	 * @var string
	 */
	var $_name = '';
	var $Title = '';

	/**
	 * Name of the layout template, usually from folder _inc/tpl/
	 *
	 * @var string
	 */
	var $_layout = '';

	/**
	 * Current area (admin, member or visitor)
	 *
	 * @var string
	 */
	var $_area = '';

	/**
	 * Account id of current user - admin or member. 0 - for visitor
	 *
	 * @var int
	 */
	var $_accountId = 0;

	/**
	 * Breadcrumbs like (array('url' => 'title', 'url2' => 'title2'))
	 *
	 * @var array
	 */
	var $_breadcrumbs = array();

	/**
	 * Constructor
	 *
	 * @return Controller
	 */
	function Controller()
	{
	}

	/**
	 * Set name of processed module
	 *
	 * @param string $name
	 * @return  null
	 */
	function setName($name)
	{
		$this->_name = $name;
	}

	/**
	 * Set current layout
	 *
	 * @param string $layout
	 * @return  null
	 */
	function setLayout($layout)
	{
		$this->_layout = $layout;
	}

	/**
	 * Sets current account id
	 *
	 * @param int $accountId
	 * @return  null
	 */
	function setAccountId($accountId)
	{
		$this->_accountId = $accountId;
	}

	/**
	 * Adds breadcrumb
	 *
	 * @param mixed $item array (url=>title) or only title for last breadcrumb
	 */
	function addBreadcrumb($item)
	{
		if(!is_array($item))
			$item = array('#' => $item);
		$this->_breadcrumbs = $this->_breadcrumbs + $item;
	}

	/**
	 * Creates new controller instance by module and area
	 *
	 * @param string $area current area
	 * @param string $mod current module name
	 * @param string $defmod default module name
	 * @return Controller module controller instance
	 */
	static function factory($area, $mod, $defmod='content')
	{
		if (!preg_match('/^\w+$/',$mod) || !file_exists(_cfg('path').'_inc/mod/'.$mod.'/controller/'.$area.'.php'))
			$mod = $defmod;

		require_once ('mod/'.$mod.'/controller/'.$area.'.php');

		$className = Controller::_formatName($mod).'_Controller_'.ucfirst($area);

		$controller = new $className();
		$controller->setName($mod);
		$controller->setLayout('tpl/'.$area.'_layout');
		$controller->addBreadcrumb(array("/$area/index.php?mod=$mod" => _fmt("$mod.title")));
		$controller->_area = $area;

		return $controller;
	}

	/**
	 * Handles each request to the sustem.
	 *
	 * @param Request $req
	 * @return View
	 */
	function process($req)
	{
		$method = 'process'.Controller::_formatName($req->getAction('act'));
		if (!method_exists($this, $method))
			$method = 'processIndex';

		_includeJs($this->_area."/".$this->_name."_".strtolower(str_replace('process','', $method)).".js");
		_includeCss($this->_area."/".$this->_name."_".strtolower(str_replace('process','', $method)).".css");
		$url = new Url();
		$subview = $this->$method($req);
		$subview->setVar('currentUrl', $url->getUrl());
		$view = new View($this->_layout);
		if (strlen($this->Title) == 0)
			$view->setVar('pageTitle', _fmt('app.title') . ':' .join('::', $this->_breadcrumbs));
		else
			$view->setVar('pageTitle', $this->Title);
		$view->setVar('breadcrumbs', $this->_breadcrumbs);
		$view->setVar('metaTitle', $subview->getVar('metaTitle'));
		$view->setVar('metaKw', $subview->getVar('metaKw'));
		$view->setVar('metaDescr', $subview->getVar('metaDescr'));
		$view->setVar('css', $subview->getVar('css'));
		$view->setVar('act', $req->get('act'));

		$msg = array();
		if ($_SESSION["show_msg"]){
			$msg[] = $_SESSION["show_msg"];
			unset($_SESSION["show_msg"]);
		}
		$msg1 = $req->getMessages($url->getUrl());
		$msg1[0] = array_merge($msg1[0], $msg);
		$view->setVar('globalMess', $msg1);
		$view->setVar('globalErr', $this->getErrors());
		$view->setVar('html', $subview->fetch(), false);
		return $view;
	}

	/**
	 * Creates view for current module and template.
	 *
	 * @param string $tpl template name
	 * @return View
	 */
	function getView($tpl)
	{
		$view = new View('mod/'.$this->_name.'/tpl/'.$tpl);
		return $view;
	}

	/**
	 * Get instance of model class by its filename. For mod/content/model/page.php use filename='page'.
	 *
	 * @param string $filename
	 * @return Db_Table
	 */
	function getModel($filename)
	{
		require_once ('mod/'.$this->_name.'/model/'.$filename.'.php');

		$className  = Controller::_formatName($this->_name);
		$className .= '_Model_';
		$className .= Controller::_formatName($filename);

		$obj = new $className();
		return $obj;
	}

	/**
	 * Formats string to CamelCaps
	 *
	 * @param string $name string to format
	 * @return string formatted string
	 */
	static function _formatName($name)
	{
		$name = str_replace(array('-', '_', '.'), ' ', strtolower($name));
		$name = preg_replace('[^a-z0-9 ]', '', $name);
		return str_replace(' ', '', ucwords($name));
	}

	/**
	 * Process page with sorter,pager,filter.
	 *
	 * @param Request $req
	 * @param string $modelName
	 * @param View $view
	 * @param array $filterCfg
	 * @param array $where static condition, like array('account_id'=>'=5')
	 * @return null
	 */
	function _processList($req, $modelName,$view, $filterCfg=array(), $where=array(),$html = TRUE,$sorter = array())
	{
		$obj = $this->getModel($modelName);
		$url  = new Url();
		$filter = new Filter($modelName, $filterCfg);
		$filter->process($req);

		$where = array_merge($filter->getCriteria(), $where);

		$pager = new Pager($url, _cfg($this->_area.'.pager')); // _cfg($this->_area.'.pager')
		if(!$sorter){
			$sorter = new Sorter($url);
			$sorter_flag = true;
		}

		$rows = $obj->findBy($where, $pager, $sorter);
		if($sorter_flag)
			$view->setVar('srtInfo', $sorter->getInfo());
		$view->setVar('fltInfo', $filter->getInfo());

		for ($i=0, $n=sizeof($rows); $i<$n; ++$i)
		{
			if (isset($rows[$i]['id']))
				$url->setParam('id', $rows[$i]['id']);

			$url->setParam('act', $modelName.'_del');
			$rows[$i]['url_del'] = $url->getUrl();

			$url->setParam('act', $modelName.'_view');
			$rows[$i]['url_view'] = $url->getUrl();
		}

		//assigning vars to view
		$view->setVar('rows', $rows,$html);
		$url->setParam('act', $modelName.'_list');
		$view->setVar('pagerInfo', $pager->getInfo());
		return null;
	}

	/**
	 * Process page with object details
	 *
	 * @param Request $req
	 * @param string $modelName
	 * @param View $view
	 * @return null
	 */
	function _processView($req, $modelName,$view)
	{
		$obj = $this->getModel($modelName);
		$id = $req->getInt('id');
		$data = $obj->find($id);
		if (!$data)
			$data = $obj->getNew();

		$view->setVar('data', $data);
		$view->setVar('id', $id);
		return null;
	}

	/**
	 * Validate, Inserts/updates record in db, handle errors
	 *
	 * @param Request $req
	 * @param string $modelName
	 * @param View $view
	 * @param array $images to upload
	 * @param bool $forward forward or return
	 * @return int 0 on error, id if ok
	 */
	function _processEdit($req, $modelName,$view, $images=array(), $forward=true, $forwardUrl = '', $files = array())
	{
		require_once 'lib/file/image.php';
		require_once 'lib/db/image.php';
		$val  = $view->getVar('validator');
		$data = $req->getArray('data');
		do
		{
			if (!$val->isValid($req->getArgs()))
			{
				$this->addErrors($val->getErrors());
				break;
			}


			$id   = $req->getInt('id');
			// check images
			$fileObjects = array();
			foreach ($images as $imgType => $isRequired)
			{
				$info      = $this->_getImageInfo($req, $imgType);
				$fileImage = new Image($info);
				$isUploaded = $fileImage->isUploaded($imgType);
				if (!$isUploaded && $isRequired && !$id) {
					$this->addError('file.not_uploaded', $info['name']);
				}
				$path = $info['path'];
				if ($isUploaded)
				{
					if ($fileImage->upload($imgType, $path))
						$fileObjects[$imgType] = $fileImage;
					else
						$this->addErrors($fileImage->getErrors());
				}
			}
			if ($this->hasErrors())
				break;
			// check files
			$fileObjects1 = array();
			foreach ($files as $imgType => $isRequired)
			{
				$info1      = $this->_getFileInfo($req, $imgType);
				$fileImage1 = new File($info1);
				$isUploaded1 = $fileImage->isUploaded($imgType);
				if (!$isUploaded1 && $isRequired)
				{
					$this->addError('file.not_uploaded', $info1['name']);
				}
				$path1 = $info1['path'];
				if ($isUploaded1)
				{
					if ($fileImage1->upload($imgType, $path1))
						$fileObjects1[$imgType] = $fileImage1;
					else
						$this->addErrors($fileImage->getErrors());
				}
			}

			if ($this->hasErrors())
				break;

			if (isset($data['pass']))
			{
				if (empty($data['pass']))
					unset($data['pass']);
				else
					$data['pass'] = md5($data['pass']);
			}

			$obj  = $this->getModel($modelName);
			if ($id)
				$result = $obj->update($data, $id);
			else
			{
				if (empty($data['cdate']))
					$data['cdate'] = Db::date();
				$result = $obj->insert($data);
			}
			if (!$result)
			{
				$this->addErrors($obj->getErrors());
				break;
			}

			$id = $id ? $id : $result;
			// move images
			foreach ($fileObjects as $imgType => $fileImage)
			{
				$dbImage       = new Db_Image($imgType);

				//insert image
				$row = array();
				$row['object_id']   = $id;
				$row['w']           = $fileImage->w;
				$row['h']           = $fileImage->h;
				$row['mime']        = $fileImage->type;
				//clear old if any
				$null = null;
				$where = array(
					'object_type' => '="' . $imgType . '"',
					'object_id'   => '="' . $id . '"',
					'pos'         => '=1'
				);
				$old = $dbImage->findBy($where, $null, $null);
				if ($old)
					$dbImage->delete($old[0]['id']);
				// save data in db table
				$imgId = $dbImage->insert($row);
				if (!$imgId)
					continue; // @todo handle errrors

				//update main record if corresponding key exists
				$obj->update(array($imgType . '_id' => $imgId), $id);

				// save original file
				$fileImage->move($path . $imgType . '/' . $imgId . '.' . $fileImage->type);
				// save thumbnails if any
				foreach ($info['thumbs'] as $prefix => $thumb)
				{
			/*if($imgType == 'logo') {
			if($row['w'] > $row['h']) $ratio = 53/$row['w'];
						else
				$ratio = 53/$row['h'];
				$thumb['w'] = round($row['w'] * $ratio);
				$thumb['h'] = round($row['h'] * $ratio);
					}*/
					$fileImage->makeThumbnail($path . $imgType . '/' . $imgId . $prefix . '.' . $fileImage->type, $thumb['w'], $thumb['h']);
				}
			}
			// move files
			foreach ($fileObjects1 as $imgType => $fileImage)
			{
				$dbImage       = new Db_File($imgType);

				//insert image
				$row = array();
				$row['object_id']   = $id;
				$row['size']        = $fileImage->size;
				$row['mime']        = trim($fileImage->type, '.');
				//clear old if any
				$null = null;
				$where = array(
					'object_type' => '="' . $imgType . '"',
					'object_id'   => '="' . $id . '"',
					'pos'         => '=1'
				);
				$old = $dbImage->findBy($where, $null, $null);
				if ($old)
					$dbImage->delete($old[0]['id']);
				// save data in db table
				$imgId = $dbImage->insert($row);
				if (!$imgId)
					continue; // @todo handle errrors

				//update main record if corresponding key exists
				$obj->update(array($imgType . '_id' => $imgId), $id);

				// save original file
				$fileImage->move($path1 . $imgType . '/' . $imgId . '' . $fileImage->type);
			}
			// all is ok
			if (!$forward)
				return $id;
			// go to list
			if ($forwardUrl == '')
			{
				$url  = new Url();
				$url->setParam('id');
				$url->setParam('act', $modelName.'_list');
				$forwardUrl = $url->getUrl();
			}else {
				$url  = new Url($forwardUrl);
				$forwardUrl = $url->getUrl();
			}
			$req->forward($forwardUrl,_fmt('app.ok'),array());

		} while(false);
		$data_var = $view->getVar('data');
		if($data_var)
			$view->setVar('data', array_merge($data_var, $data));
		return 0;
	}

	/**
	 * Deletes record(s) in db, handle errors
	 * @deprecated see _processArrayAction
	 * @param Request $req
	 * @param string $modelName
	 * @return null
	 */
	function _processDel($req, $modelName)
	{
		return $this->_processArrayAction($req, $modelName);
	}

	/**
	 * Perform array action (delete, approve, decline)
	 *
	 * @param Request $req
	 * @param string $modelName
	 * @param string $action action name
	 * @return null
	 */
	function _processArrayAction($req, $modelName, $action='delete')
	{
		$obj = $this->getModel($modelName);
		$ids = $req->getArray('id');

		$errs = array();
		$mess = array();
		foreach ($ids as $id)
		{
			$id = intVal($id);
			if ($obj->$action($id))
				$mess[] = _fmt($this->_name . '.mes.' . $modelName . '_' . $action . '_ok', $id);
			else
				$errs[] = _fmt($this->_name . '.mes.' . $modelName . '_' . $action . '_err', $id);
		}

		$url = new Url();
		$url->setParam('act', $modelName.'_list');
		$url->setParam('id');
		$req->forward($url->getUrl(), $mess, $errs);
		return null;
	}

	/**
	 * Base function to display object
	 * images list
	 *
	 * @param Request $req
	 * @return View
	 */
	function _processImagesList($req, $modelName,$view)
	{
		require_once 'lib/db/image.php';
		$dbImage = new Db_Image($modelName);

		$id = $req->getInt('id');

		$images = $dbImage->getImages($id);
		$url = new Url();
		$url->setParam('act', 'image_delete');

		//position options
		$posOpt = array();
		$position = array();

		for ($i=0; $i < count ($images); $i++)
		{
			$position[$images[$i]['id']] = $i + 1;

			$url->setParam('img_id', $images[$i]['id']);
			$images[$i]['delete_url'] = $url->getUrl();

			$posOpt[$i+1] = $i+1;
		}

		$view->setVar('imagePosition', $position);
		$view->setVar('imagePosOpt', $posOpt);

		//images
		$view->setVar('images', $images);
		$view->setVar('id', $id);
		$view->setVar('fileInputName', $modelName);

		return $view;
	}

	/**
	 * Base function to display object
	 * files list
	 *
	 * @param Request $req
	 * @return View
	 */
	function _processFilesList($req, $modelName,$view)
	{
		require_once 'lib/db/file.php';
		$dbImage = new Db_File($modelName);

		$id = $req->getInt('id');

		$images = $dbImage->getFiles($id);
		$url = new Url();
		$url->setParam('act', 'file_delete');

		//position options
		$posOpt = array();
		$position = array();

		for ($i=0; $i < count ($images); $i++)
		{
			$position[$images[$i]['id']] = $i + 1;

			$url->setParam('file_id', $images[$i]['id']);
			$images[$i]['delete_url'] = $url->getUrl();

			$posOpt[$i+1] = $i+1;
		}

		$view->setVar('filePosition', $position);
		$view->setVar('filePosOpt', $posOpt);

		//images
		$view->setVar('files', $images);
		$view->setVar('id', $id);
		$view->setVar('fileInputName', $modelName);

		return $view;
	}
	/**
	 * Base function to support image upload for object
	 * forwards to current page with error or ok message
	 *
	 * @param Request $req
	 * @param string $imageType image type
	 * @param bool $forward forward or return
	 * @return null. Forwards to the same page with ok/err message
	 */
	function _processImageUpload($req, $imageType, $forward=true, $fieldName = false)
	{
		$err  = array();
		$mess = array();
		$info = $this->_getImageInfo($req, $imageType);

		require_once 'lib/db/image.php';
		require_once 'lib/file/image.php';
		$dbImage       = new Db_Image($imageType);
		$fileImage     = new Image($info);
		$imgId         = 0;
		$id = $req->getInt('id');
		if ($fileImage->isUploaded($fieldName? $fieldName : $imageType) )
		{
			$path = $info['path'] . $imageType . '/';
			if ($fileImage->upload($fieldName? $fieldName : $imageType, $path))
			{
				$row = array();
				$row['object_id']   = $id;
				$row['w']           = $fileImage->w;
				$row['h']           = $fileImage->h;
				$row['mime']        = $fileImage->type;
				// save data in db table
				$imgId = $dbImage->insert($row);
				if ($imgId)
				{
					//save original file
					$fileImage->move($path . $imgId . '.' . $fileImage->type);

					//make thumbnails preview
					foreach ($info['thumbs'] as $prefix => $thumb)
					{
						$fileImage->makeThumbnail($path . $imgId . $prefix . '.' . $fileImage->type, $thumb['w'], $thumb['h']);
					}
					$mess = _fmt('app.ok');

				}
				else
				{
					$err = $dbImage->getErrors();
				}
			}
			else
			{
				$err = $fileImage->getErrors();
			}
		}

		if (!$forward)
		{
			$this->addErrors($err);
			return $imgId;
		}

		$url = new Url();
		$req->forward($url->getUrl(), $mess, $err);
	}


	/**
	 * Base function to support file upload for object
	 * forwards to current page with error or ok message
	 *
	 * @param Request $req
	 * @param string $fileType image type
	 * @param bool $forward forward or return
	 * @return null. Forwards to the same page with ok/err message
	 */
	function _processFileUpload($req, $fileType, $forward=true)
	{
		$err  = array();
		$mess = array();
		$info = $this->_getFileInfo($req, $fileType);

		require_once 'lib/db/file.php';
		require_once 'lib/file.php';
		$dbImage       = new Db_File($imageType);
		$fileImage     = new File($info);
		$imgId         = 0;

		$id = $req->getInt('id');

		if ($fileImage->isUploaded($imageType) )
		{
			$path = $info['path'] . $imageType . '/';
			if ($fileImage->upload($imageType, $path))
			{
				$row = array();
				$row['object_id']   = $id;
				$row['size']        = $fileImage->size;
				$row['mime']        = $fileImage->type;
				// save data in db table
				$imgId = $dbImage->insert($row);
				if ($imgId)
				{
					//save original file
					$fileImage->move($path . $imgId . '.' . $fileImage->type);

					$mess = _fmt('app.ok');

				}
				else
				{
					$err = $dbImage->getErrors();
				}
			}
			else
			{
				$err = $fileImage->getErrors();
			}
		}

		if (!$forward)
		{
			$this->addErrors($err);
			return $imgId;
		}

		$url = new Url();
		$req->forward($url->getUrl(), $mess, $err);
	}
	/**
	 * Base function to support image deletion for objects
	 * forwards to current page with error or ok message
	 *
	 * @param Request $req
	 * @param string model name
	 */
	function _processImageDelete($req, $modelName)
	{
		$err  = array();
		$mess = array();

		require_once 'lib/db/image.php';
		$dbImage = new Db_Image('product');

		$imgId = $req->getInt('img_id');

		$url = new Url();
		$url->setParam('img_id');
		$url->setParam('act', $modelName . '_view');

		if ($dbImage->delete($imgId))
		{
			$mess[] = _fmt('image.deletion.ok', $imgId);
		}
		else
		{
			$err[] = _fmt('image.deletion.err', $imgId); //$dbImage->getErrors()
		}
		$req->forward($url->getUrl(), $mess, $err);
	}

	/**
	 * Base function to support file deletion for objects
	 * forwards to current page with error or ok message
	 *
	 * @param Request $req
	 * @param string model name
	 */
	function _processFileDelete($req, $modelName)
	{
		$err  = array();
		$mess = array();

		require_once 'lib/db/file.php';
		$dbImage = new Db_File('resume');

		$imgId = $req->getInt('file_id');

		$url = new Url();
		$url->setParam('file_id');
		$url->setParam('act', $modelName . '_view');

		if ($dbImage->delete($imgId))
		{
			$mess[] = _fmt('file.deletion.ok', $imgId);
		}
		else
		{
			$err[] = _fmt('file.deletion.err', $imgId); //$dbImage->getErrors()
		}
		$req->forward($url->getUrl(), $mess, $err);
	}
	/**
	 * Change images positions
	 *
	 * @param Request $req
	 * @return null on error or forwards if all is ok
	 */
	function _processImagesPos($req, $modelName,$view)
	{
		$position = $req->getArray('position');
		$position = array_map('intVal', $position);

		require_once 'lib/db/image.php';
		$dbImage = new Db_Image($modelName);
		if ($dbImage->updatePos($position))
		{
			$url = new Url();
			$req->forward($url->getUrl(), _fmt('image.position.ok'));
		}
		else
		{
			$this->addError('image.position.err','');
			$view->setVar('imagePosition', $position);
		}
		return null;
	}

	/**
	 * Gets info about uploadable image: h,w,path ...)
	 *
	 * @param Request $req current request
	 * @param string $modelName model name (image type)
	 * @return array
	 */
	function _getImageInfo($req, $imageType)
	{
		return _cfg('image.' . $imageType);
	}

	/**
	 * Gets info about uploadable file: size,path ...)
	 *
	 * @param Request $req current request
	 * @param string $modelName model name (image type)
	 * @return array
	 */
	function _getFileInfo($req, $fileType)
	{
		return _cfg('file.' . $fileType);
	}
}